//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
// Target Devices:
// Tool versions:
//
// Create Date:    2011-08-18 15:28
// Project Name:
// Description:
//
// Dependencies:
//
// Revision: 1.0
// Revision 0.01 - File Created
//
//
//////////////////////////////////////////////////////////////////////////////////
`timescale 1ns/1ns
module sobel_top
#(
    parameter H_SIZE = 320,
    parameter V_SIZE = 240
)
(
    input               cam_pclk,    // use cam_pclk
    input               sys_clk,
    input               rst,

    input               wrreq,
    input [7:0]         wrdata,

    output reg[15:0]    out_data
);

/********************************************************\
Parameter
\********************************************************/
localparam U_DLY        = 1;
localparam H_SIZE_0     = H_SIZE - 1;

/********************************************************\
Signals
\********************************************************/
wire [7:0]  line0_data,line1_data,line2_data;
reg [7:0]   p0,p1,p2,p3,p4,p5,p6,p7,p8;
wire [7:0]  out;

reg         sobel_valid;
reg [7:0]   sobel_data;

// sync signal
reg         cam_pclk_dly1;
reg         cam_pclk_dly2;
wire        cam_pclk_rising;

reg         buff_rd_en;
reg         buff_wr;
reg         buff_rd1;
reg         buff_rd2;
reg         buff_rd2_dly1;
reg         buff_rd2_dly2;
wire        buff_rd;
reg [7:0]   buff_addr1;
reg [7:0]   buff_addr2;
wire [7:0]  buff_addr;
wire [15:0] buff_rddata;
reg [15:0]  buff_wrdata;

reg [9:0]   line_cnt;
reg         frame_done;

reg [15:0]  max_data;
reg [15:0]  max_id;
wire        max_valid;
reg         comp_rden;
wire        comp_done;

/********************************************************\
main code
\********************************************************/
assign cam_pclk_rising  = ~cam_pclk_dly2 & cam_pclk_dly1;
assign buff_rd          = (frame_done==1'b1)?buff_rd2:buff_rd1;
assign buff_addr        = (frame_done==1'b1)?buff_addr2:buff_addr1;
assign max_valid        = (max_data < buff_rddata);
assign comp_done        = buff_addr2 == H_SIZE_0;

always@(posedge cam_pclk)
begin
    if(rst==1'b1)
    begin
        sobel_valid <= 1'b0;
        sobel_data  <= 'h0;
    end
    else
    begin
        sobel_valid <= #U_DLY wrreq;
        sobel_data  <= #U_DLY out;
    end
end

always@(posedge cam_pclk)
begin
    p0  <= #U_DLY p1;
    p1  <= #U_DLY p2;
    p2  <= #U_DLY line2_data;
    p3  <= #U_DLY p4;
    p4  <= #U_DLY p5;
    p5  <= #U_DLY line1_data;
    p6  <= #U_DLY p7;
    p7  <= #U_DLY p8;
    p8  <= #U_DLY line0_data;
end

sobel_op u_sobel(
    .p0     (p0),
    .p1     (p1),
    .p2     (p2),
    .p3     (p3),
    .p5     (p5),
    .p6     (p6),
    .p7     (p7),
    .p8     (p8),
    .out    (out)
);

shift_tabs u_shift
(
    .clk            (cam_pclk),    // use cam_pclk
    .rst            (rst),

    .shift_en       (wrreq),
    .shift_data     (wrdata),

    .line0_data_o   (line0_data),
    .line1_data_o   (line1_data),
    .line2_data_o   (line2_data)
);

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        cam_pclk_dly1 <= 1'b0;
        cam_pclk_dly2 <= 1'b0;
    end
    else
    begin
        cam_pclk_dly1 <= #U_DLY cam_pclk;
        cam_pclk_dly2 <= #U_DLY cam_pclk_dly1;
    end
end

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        buff_rd_en  <= 1'b1;
    end
    else if(buff_rd1)
    begin
        buff_rd_en  <= #U_DLY 1'b0;
    end
    else if(buff_wr)
    begin
        buff_rd_en  <= #U_DLY 1'b1;
    end
end

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        buff_rd1 <= 1'b0;
    end
    else if(buff_rd_en)
    begin
        buff_rd1 <= #U_DLY 1'b1;
    end
    else
    begin
        buff_rd1 <= #U_DLY 1'b0;
    end
end

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        buff_wrdata <= 'h0;
        buff_wr    <= 1'b0;
    end
    else if(sobel_valid & cam_pclk_rising)
    begin
        buff_wrdata  <= #U_DLY buff_rddata + sobel_data[7:4];
        buff_wr     <= #U_DLY 1'b1;
    end
    else
    begin
        buff_wr     <= #U_DLY 1'b0;
    end
end

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        buff_addr1  <= 'h0;
    end
    else if(buff_wr)
    begin
        buff_addr1  <= #U_DLY (buff_addr1 >= H_SIZE_0)?'h0:(buff_addr1 + 1'b1);
    end
end

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        line_cnt    <= 'h0;
    end
    else if(comp_done)
    begin
        line_cnt  <= #U_DLY 'h0;
    end
    else if(buff_wr)
    begin
        line_cnt  <= #U_DLY line_cnt + (buff_addr1==H_SIZE_0);
    end
end

/********************* find the index of the max value *************************************/

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        frame_done  <= 1'b0;
    end
    else if(comp_done)
    begin
        frame_done  <= #U_DLY 1'b0;
    end
    else if(line_cnt == V_SIZE)
    begin
        frame_done    <= #U_DLY 1'b1;
    end
end

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        comp_rden   <= 1'b1;
    end
    else if(buff_rd2)
    begin
        comp_rden   <= #U_DLY 1'b0;
    end
    else if(buff_rd2_dly2)
    begin
        comp_rden  <= #U_DLY 1'b1;
    end
end

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        buff_rd2    <= 1'b0;
    end
    else if(frame_done & comp_rden)
    begin
        buff_rd2    <= #U_DLY 1'b1;
    end
    else
    begin
        buff_rd2    <= #U_DLY 1'b0;
    end
end

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        buff_rd2_dly1   <= 1'b0;
        buff_rd2_dly2   <= 1'b0;
    end
    else
    begin
        buff_rd2_dly1   <= #U_DLY buff_rd2;
        buff_rd2_dly2   <= #U_DLY buff_rd2_dly1;
    end
end


always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        buff_addr2    <= 'h0;
    end
    else if(~buff_rd2_dly2 & buff_rd2_dly1) // buff_rd2 rising
    begin
        buff_addr2  <= #U_DLY buff_addr2 + 1'b1;
    end
end

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        max_data    <= 'h0;
    end
    else if(buff_rd2_dly1)
    begin
        max_data  <= #U_DLY (max_valid==1'b1)?buff_rddata:max_data;
    end
end

always@(posedge sys_clk,posedge rst)
begin
    if(rst==1'b1)
    begin
        max_id    <= 'h0;
    end
    else if(max_valid)
    begin
        max_id  <= #U_DLY buff_addr2;
    end
end

always@(posedge sys_clk)
begin
    if(comp_done)
    begin
        out_data    <= #U_DLY max_id;
    end
end

BUFFER_256x16_DRAM u_buff(
  .a            (buff_addr),
  .d            (buff_wrdata),
  .clk          (sys_clk),
  .we           (buff_wr),
  .qspo_ce      (buff_rd),
  .qspo_rst     (rst),
  .qspo         (buff_rddata)
);

endmodule
